home *** CD-ROM | disk | FTP | other *** search
- ; -*- fundamental -*- (asm-mode sucks)
- ; $Id: pxelinux.asm,v 1.167 2005/01/12 00:34:54 hpa Exp $
- ; ****************************************************************************
- ;
- ; pxelinux.asm
- ;
- ; A program to boot Linux kernels off a TFTP server using the Intel PXE
- ; network booting API. It is based on the SYSLINUX boot loader for
- ; MS-DOS floppies.
- ;
- ; Copyright (C) 1994-2005 H. Peter Anvin
- ;
- ; This program is free software; you can redistribute it and/or modify
- ; it under the terms of the GNU General Public License as published by
- ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- ; Boston MA 02111-1307, USA; either version 2 of the License, or
- ; (at your option) any later version; incorporated herein by reference.
- ;
- ; ****************************************************************************
-
- %define IS_PXELINUX 1
- %include "macros.inc"
- %include "config.inc"
- %include "kernel.inc"
- %include "bios.inc"
- %include "tracers.inc"
- %include "pxe.inc"
- %include "layout.inc"
-
- ;
- ; Some semi-configurable constants... change on your own risk.
- ;
- my_id equ pxelinux_id
- FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
- FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
- NULLFILE equ 0 ; Zero byte == null file name
- NULLOFFSET equ 4 ; Position in which to look
- REBOOT_TIME equ 5*60 ; If failure, time until full reset
- %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
- MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
- MAX_OPEN equ (1 << MAX_OPEN_LG2)
- PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
- TFTP_PORT equ htons(69) ; Default TFTP port
- PKT_RETRY equ 6 ; Packet transmit retry count
- PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
- ; Desired TFTP block size
- ; For Ethernet MTU is normally 1500. Unfortunately there seems to
- ; be a fair number of networks with "substandard" MTUs which break.
- ; The code assumes TFTP_LARGEBLK <= 2K.
- TFTP_MTU equ 1472
- TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
- ; Standard TFTP block size
- TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
- TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
- %assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
-
- SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
- SECTOR_SIZE equ TFTP_BLOCKSIZE
-
- ;
- ; This is what we need to do when idle
- ;
- %macro RESET_IDLE 0
- call reset_idle
- %endmacro
- %macro DO_IDLE 0
- call check_for_arp
- %endmacro
-
- ;
- ; TFTP operation codes
- ;
- TFTP_RRQ equ htons(1) ; Read request
- TFTP_WRQ equ htons(2) ; Write request
- TFTP_DATA equ htons(3) ; Data packet
- TFTP_ACK equ htons(4) ; ACK packet
- TFTP_ERROR equ htons(5) ; ERROR packet
- TFTP_OACK equ htons(6) ; OACK packet
-
- ;
- ; TFTP error codes
- ;
- TFTP_EUNDEF equ htons(0) ; Unspecified error
- TFTP_ENOTFOUND equ htons(1) ; File not found
- TFTP_EACCESS equ htons(2) ; Access violation
- TFTP_ENOSPACE equ htons(3) ; Disk full
- TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
- TFTP_EBADID equ htons(5) ; Unknown transfer
- TFTP_EEXISTS equ htons(6) ; File exists
- TFTP_ENOUSER equ htons(7) ; No such user
- TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
-
- ;
- ; The following structure is used for "virtual kernels"; i.e. LILO-style
- ; option labels. The options we permit here are `kernel' and `append
- ; Since there is no room in the bottom 64K for all of these, we
- ; stick them at vk_seg:0000 and copy them down before we need them.
- ;
- struc vkernel
- vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
- vk_rname: resb FILENAME_MAX ; Real name
- vk_ipappend: resb 1 ; "IPAPPEND" flag
- resb 1 ; Pad
- vk_appendlen: resw 1
- alignb 4
- vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
- vk_end: equ $ ; Should be <= vk_size
- endstruc
-
- ;
- ; Segment assignments in the bottom 640K
- ; 0000h - main code/data segment (and BIOS segment)
- ;
- real_mode_seg equ 4000h
- vk_seg equ 3000h ; Virtual kernels
- xfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mem
- pktbuf_seg equ 1000h ; Packet buffers segments
- comboot_seg equ real_mode_seg ; COMBOOT image loading zone
-
- ;
- ; BOOTP/DHCP packet pattern
- ;
- struc bootp_t
- bootp:
- .opcode resb 1 ; BOOTP/DHCP "opcode"
- .hardware resb 1 ; ARP hardware type
- .hardlen resb 1 ; Hardware address length
- .gatehops resb 1 ; Used by forwarders
- .ident resd 1 ; Transaction ID
- .seconds resw 1 ; Seconds elapsed
- .flags resw 1 ; Broadcast flags
- .cip resd 1 ; Client IP
- .yip resd 1 ; "Your" IP
- .sip resd 1 ; Next server IP
- .gip resd 1 ; Relay agent IP
- .macaddr resb 16 ; Client MAC address
- .sname resb 64 ; Server name (optional)
- .bootfile resb 128 ; Boot file name
- .option_magic resd 1 ; Vendor option magic cookie
- .options resb 1260 ; Vendor options
- endstruc
-
- BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
-
- ;
- ; TFTP connection data structure. Each one of these corresponds to a local
- ; UDP port. The size of this structure must be a power of 2.
- ; HBO = host byte order; NBO = network byte order
- ; (*) = written by options negotiation code, must be dword sized
- ;
- struc open_file_t
- tftp_localport resw 1 ; Local port number (0 = not in use)
- tftp_remoteport resw 1 ; Remote port number
- tftp_remoteip resd 1 ; Remote IP address
- tftp_filepos resd 1 ; Bytes downloaded (including buffer)
- tftp_filesize resd 1 ; Total file size(*)
- tftp_blksize resd 1 ; Block size for this connection(*)
- tftp_bytesleft resw 1 ; Unclaimed data bytes
- tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
- tftp_dataptr resw 1 ; Pointer to available data
- resw 2 ; Currently unusued
- ; At end since it should not be zeroed on socked close
- tftp_pktbuf resw 1 ; Packet buffer offset
- endstruc
- %ifndef DEPEND
- %if (open_file_t_size & (open_file_t_size-1))
- %error "open_file_t is not a power of 2"
- %endif
- %endif
-
- ; ---------------------------------------------------------------------------
- ; BEGIN CODE
- ; ---------------------------------------------------------------------------
-
- ;
- ; Memory below this point is reserved for the BIOS and the MBR
- ;
- section .earlybss
- trackbufsize equ 8192
- trackbuf resb trackbufsize ; Track buffer goes here
- getcbuf resb trackbufsize
- ; ends at 4800h
-
- ; Put some large buffers here, before RBFG_brainfuck,
- ; where we can still carefully control the address
- ; assignments...
-
- alignb open_file_t_size
- Files resb MAX_OPEN*open_file_t_size
-
- alignb FILENAME_MAX
- BootFile resb 256 ; Boot file from DHCP packet
- ConfigServer resd 1 ; Null prefix for mangled config name
- ConfigName resb 256-4 ; Configuration file from DHCP option
- PathPrefix resb 256 ; Path prefix derived from boot file
- DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
- IPOption resb 80 ; ip= option buffer
- InitStack resd 1 ; Pointer to reset stack
-
- ; Warning here: RBFG build 22 randomly overwrites memory location
- ; [0x5680,0x576c), possibly more. It seems that it gets confused and
- ; screws up the pointer to its own internal packet buffer and starts
- ; writing a received ARP packet into low memory.
- RBFG_brainfuck resb 0E00h
-
- section .bss
- alignb 4
- RebootTime resd 1 ; Reboot timeout, if set by option
- StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
- APIVer resw 1 ; PXE API version found
- IPOptionLen resw 1 ; Length of IPOption
- IdleTimer resw 1 ; Time to check for ARP?
- LocalBootType resw 1 ; Local boot return code
- PktTimeout resw 1 ; Timeout for current packet
- RealBaseMem resw 1 ; Amount of DOS memory after freeing
- OverLoad resb 1 ; Set if DHCP packet uses "overloading"
-
- ; The relative position of these fields matter!
- MACLen resb 1 ; MAC address len
- MACType resb 1 ; MAC address type
- MAC resb 16 ; Actual MAC address
- BOOTIFStr resb 7 ; Space for "BOOTIF="
- MACStr resb 3*17 ; MAC address as a string
-
- ;
- ; PXE packets which don't need static initialization
- ;
- alignb 4
- pxe_unload_stack_pkt:
- .status: resw 1 ; Status
- .reserved: resw 10 ; Reserved
- pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
-
- alignb 16
- ; BOOTP/DHCP packet buffer
-
- alignb 16
- packet_buf resb 2048 ; Transfer packet
- packet_buf_size equ $-packet_buf
-
- ;
- ; Constants for the xfer_buf_seg
- ;
- ; The xfer_buf_seg is also used to store message file buffers. We
- ; need two trackbuffers (text and graphics), plus a work buffer
- ; for the graphics decompressor.
- ;
- xbs_textbuf equ 0 ; Also hard-coded, do not change
- xbs_vgabuf equ trackbufsize
- xbs_vgatmpbuf equ 2*trackbufsize
-
- section .text
- ;
- ; PXELINUX needs more BSS than the other derivatives;
- ; therefore we relocate it from 7C00h on startup.
- ;
- StackBuf equ $ ; Base of stack if we use our own
-
- ;
- ; Primary entry point.
- ;
- bootsec equ $
- _start:
- pushfd ; Paranoia... in case of return to PXE
- pushad ; ... save as much state as possible
- push ds
- push es
- push fs
- push gs
-
- xor ax,ax
- mov ds,ax
- mov es,ax
-
- ; This is uglier than it should be, but works around
- ; some NASM 0.98.38 bugs.
- mov di,section..bcopy32.start
- add di,__bcopy_size-4
- lea si,[di-(TEXT_START-7C00h)]
- lea cx,[di-(TEXT_START-4)]
- shr cx,2
- std ; Overlapping areas, copy backwards
- rep movsd
-
- jmp 0:_start1 ; Canonicalize address
- _start1:
- mov bp,sp
- les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
-
- ; That is all pushed onto the PXE stack. Save the pointer
- ; to it and switch to an internal stack.
- mov [InitStack],sp
- mov [InitStack+2],ss
-
- %if USE_PXE_PROVIDED_STACK
- ; Apparently some platforms go bonkers if we
- ; set up our own stack...
- mov [BaseStack],sp
- mov [BaseStack+4],ss
- %endif
-
- cli ; Paranoia
- lss esp,[BaseStack]
-
- sti ; Stack set up and ready
- cld ; Copy upwards
-
- ;
- ; Initialize screen (if we're using one)
- ;
- ; Now set up screen parameters
- call adjust_screen
-
- ; Wipe the F-key area
- mov al,NULLFILE
- mov di,FKeyName
- mov cx,10*(1 << FILENAME_MAX_LG2)
- push es ; Save ES -> PXE structure
- push ds ; ES <- DS
- pop es
- rep stosb
- pop es ; Restore ES
-
- ;
- ; Tell the user we got this far
- ;
- mov si,syslinux_banner
- call writestr
-
- mov si,copyright_str
- call writestr
-
- ;
- ; Assume API version 2.1, in case we find the !PXE structure without
- ; finding the PXENV+ structure. This should really look at the Base
- ; Code ROM ID structure in have_pxe, but this is adequate for now --
- ; if we have !PXE, we have to be 2.1 or higher, and we don't care
- ; about higher versions than that.
- ;
- mov word [APIVer],0201h
-
- ;
- ; Now we need to find the !PXE structure. It's *supposed* to be pointed
- ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
- ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
- ; We should make that the second test, and not trash ES:BX...
- ;
- cmp dword [es:bx], '!PXE'
- je have_pxe
-
- ; Uh-oh, not there... try plan B
- mov ax, 5650h
- int 1Ah ; May trash regs
- jc no_pxe
- cmp ax,564Eh
- jne no_pxe
-
- ; Okay, that gave us the PXENV+ structure, find !PXE
- ; structure from that (if available)
- cmp dword [es:bx], 'PXEN'
- jne no_pxe
- cmp word [es:bx+4], 'V+'
- je have_pxenv
-
- ; Nothing there either. Last-ditch: scan memory
- call memory_scan_for_pxe_struct ; !PXE scan
- jnc have_pxe
- call memory_scan_for_pxenv_struct ; PXENV+ scan
- jnc have_pxenv
-
- no_pxe: mov si,err_nopxe
- call writestr
- jmp kaboom
-
- have_pxenv:
- mov [StrucPtr],bx
- mov [StrucPtr+2],es
-
- mov si,found_pxenv
- call writestr
-
- mov si,apiver_str
- call writestr
- mov ax,[es:bx+6]
- mov [APIVer],ax
- call writehex4
- call crlf
-
- cmp ax,0201h ; API version 2.1 or higher
- jb old_api
- mov si,bx
- mov ax,es
- les bx,[es:bx+28h] ; !PXE structure pointer
- cmp dword [es:bx],'!PXE'
- je have_pxe
-
- ; Nope, !PXE structure missing despite API 2.1+, or at least
- ; the pointer is missing. Do a last-ditch attempt to find it.
- call memory_scan_for_pxe_struct
- jnc have_pxe
-
- ; Otherwise, no dice, use PXENV+ structure
- mov bx,si
- mov es,ax
-
- old_api: ; Need to use a PXENV+ structure
- mov si,using_pxenv_msg
- call writestr
-
- mov eax,[es:bx+0Ah] ; PXE RM API
- mov [PXENVEntry],eax
-
- mov si,undi_data_msg
- call writestr
- mov ax,[es:bx+20h]
- call writehex4
- call crlf
- mov si,undi_data_len_msg
- call writestr
- mov ax,[es:bx+22h]
- call writehex4
- call crlf
- mov si,undi_code_msg
- call writestr
- mov ax,[es:bx+24h]
- call writehex4
- call crlf
- mov si,undi_code_len_msg
- call writestr
- mov ax,[es:bx+26h]
- call writehex4
- call crlf
-
- ; Compute base memory size from PXENV+ structure
- xor esi,esi
- movzx eax,word [es:bx+20h] ; UNDI data seg
- cmp ax,[es:bx+24h] ; UNDI code seg
- ja .use_data
- mov ax,[es:bx+24h]
- mov si,[es:bx+26h]
- jmp short .combine
- .use_data:
- mov si,[es:bx+22h]
- .combine:
- shl eax,4
- add eax,esi
- shr eax,10 ; Convert to kilobytes
- mov [RealBaseMem],ax
-
- mov si,pxenventry_msg
- call writestr
- mov ax,[PXENVEntry+2]
- call writehex4
- mov al,':'
- call writechr
- mov ax,[PXENVEntry]
- call writehex4
- call crlf
- jmp have_entrypoint
-
- have_pxe:
- mov [StrucPtr],bx
- mov [StrucPtr+2],es
-
- mov eax,[es:bx+10h]
- mov [PXEEntry],eax
-
- mov si,undi_data_msg
- call writestr
- mov eax,[es:bx+2Ah]
- call writehex8
- call crlf
- mov si,undi_data_len_msg
- call writestr
- mov ax,[es:bx+2Eh]
- call writehex4
- call crlf
- mov si,undi_code_msg
- call writestr
- mov ax,[es:bx+32h]
- call writehex8
- call crlf
- mov si,undi_code_len_msg
- call writestr
- mov ax,[es:bx+36h]
- call writehex4
- call crlf
-
- ; Compute base memory size from !PXE structure
- xor esi,esi
- mov eax,[es:bx+2Ah]
- cmp eax,[es:bx+32h]
- ja .use_data
- mov eax,[es:bx+32h]
- mov si,[es:bx+36h]
- jmp short .combine
- .use_data:
- mov si,[es:bx+2Eh]
- .combine:
- add eax,esi
- shr eax,10
- mov [RealBaseMem],ax
-
- mov si,pxeentry_msg
- call writestr
- mov ax,[PXEEntry+2]
- call writehex4
- mov al,':'
- call writechr
- mov ax,[PXEEntry]
- call writehex4
- call crlf
-
- have_entrypoint:
- push cs
- pop es ; Restore CS == DS == ES
-
- ;
- ; Network-specific initialization
- ;
- xor ax,ax
- mov [LocalDomain],al ; No LocalDomain received
-
- ;
- ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
- ; address). This lives in the DHCPACK packet (query info 2).
- ;
- query_bootp:
- mov di,pxe_bootp_query_pkt_2
- mov bx,PXENV_GET_CACHED_INFO
-
- call pxenv
- push word [pxe_bootp_query_pkt_2.status]
- jc .pxe_err1
- cmp ax,byte 0
- je .pxe_ok
- .pxe_err1:
- mov di,pxe_bootp_size_query_pkt
- mov bx,PXENV_GET_CACHED_INFO
-
- call pxenv
- jc .pxe_err
- .pxe_size:
- mov ax,[pxe_bootp_size_query_pkt.buffersize]
- call writehex4
- call crlf
-
- .pxe_err:
- mov si,err_pxefailed
- call writestr
- call writehex4
- mov al, ' '
- call writechr
- pop ax ; Status
- call writehex4
- call crlf
- jmp kaboom ; We're dead
-
- .pxe_ok:
- pop cx ; Forget status
- mov cx,[pxe_bootp_query_pkt_2.buffersize]
- call parse_dhcp ; Parse DHCP packet
- ;
- ; Save away MAC address (assume this is in query info 2. If this
- ; turns out to be problematic it might be better getting it from
- ; the query info 1 packet.)
- ;
- .save_mac:
- movzx cx,byte [trackbuf+bootp.hardlen]
- mov [MACLen],cl
- mov al,[trackbuf+bootp.hardware]
- mov [MACType],al
- mov si,trackbuf+bootp.macaddr
- mov di,MAC
- push cx
- rep movsb
- mov cx,MAC+16
- sub cx,di
- xor ax,ax
- rep stosb
-
- mov si,bootif_str
- mov di,BOOTIFStr
- mov cx,bootif_str_len
- rep movsb
-
- pop cx
- mov si,MACType
- inc cx
- mov bx,hextbl_lower
- .hexify_mac:
- lodsb
- mov ah,al
- shr al,4
- xlatb
- stosb
- mov al,ah
- and al,0Fh
- xlatb
- stosb
- mov al,'-'
- stosb
- loop .hexify_mac
- mov [di-1],byte 0 ; Null-terminate and strip final colon
-
- ;
- ; Now, get the boot file and other info. This lives in the CACHED_REPLY
- ; packet (query info 3).
- ;
- mov [pxe_bootp_size_query_pkt.packettype], byte 3
-
- mov di,pxe_bootp_query_pkt_3
- mov bx,PXENV_GET_CACHED_INFO
-
- call pxenv
- push word [pxe_bootp_query_pkt_3.status]
- jc .pxe_err1
- cmp ax,byte 0
- jne .pxe_err1
-
- ; Packet loaded OK...
- pop cx ; Forget status
- mov cx,[pxe_bootp_query_pkt_3.buffersize]
- call parse_dhcp ; Parse DHCP packet
- ;
- ; Generate ip= option
- ;
- call genipopt
-
- ;
- ; Print IP address
- ;
- mov eax,[MyIP]
- mov di,DotQuadBuf
- push di
- call gendotquad ; This takes network byte order input
-
- xchg ah,al ; Convert to host byte order
- ror eax,16 ; (BSWAP doesn't work on 386)
- xchg ah,al
-
- mov si,myipaddr_msg
- call writestr
- call writehex8
- mov al,' '
- call writechr
- pop si ; DotQuadBuf
- call writestr
- call crlf
-
- mov si,IPOption
- call writestr
- call crlf
-
- ;
- ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
- ; if we didn't get the magic enable, do not recognize any other options.
- ;
- check_dhcp_magic:
- test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
- jnz .got_magic
- mov byte [DHCPMagic], 0 ; If not, kill all other options
- .got_magic:
-
-
- ;
- ; Initialize UDP stack
- ;
- udp_init:
- mov eax,[MyIP]
- mov [pxe_udp_open_pkt.sip],eax
- mov di,pxe_udp_open_pkt
- mov bx,PXENV_UDP_OPEN
- call pxenv
- jc .failed
- cmp word [pxe_udp_open_pkt.status], byte 0
- je .success
- .failed: mov si,err_udpinit
- call writestr
- jmp kaboom
- .success:
-
- ;
- ; Common initialization code
- ;
- %include "init.inc"
- %include "cpuinit.inc"
-
- ;
- ; Now we're all set to start with our *real* business. First load the
- ; configuration file (if any) and parse it.
- ;
- ; In previous versions I avoided using 32-bit registers because of a
- ; rumour some BIOSes clobbered the upper half of 32-bit registers at
- ; random. I figure, though, that if there are any of those still left
- ; they probably won't be trying to install Linux on them...
- ;
- ; The code is still ripe with 16-bitisms, though. Not worth the hassle
- ; to take'm out. In fact, we may want to put them back if we're going
- ; to boot ELKS at some point.
- ;
-
- ;
- ; Store standard filename prefix
- ;
- prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
- jnz .got_prefix
- mov si,BootFile
- mov di,PathPrefix
- cld
- call strcpy
- mov cx,di
- sub cx,PathPrefix+1
- std
- lea si,[di-2] ; Skip final null!
- .find_alnum: lodsb
- or al,20h
- cmp al,'.' ; Count . or - as alphanum
- je .alnum
- cmp al,'-'
- je .alnum
- cmp al,'0'
- jb .notalnum
- cmp al,'9'
- jbe .alnum
- cmp al,'a'
- jb .notalnum
- cmp al,'z'
- ja .notalnum
- .alnum: loop .find_alnum
- dec si
- .notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
- cld
- .got_prefix:
- mov si,tftpprefix_msg
- call writestr
- mov si,PathPrefix
- call writestr
- call crlf
-
- ;
- ; Load configuration file
- ;
- find_config:
-
- ;
- ; Begin looking for configuration file
- ;
- config_scan:
- mov di,ConfigServer
- xor eax,eax
- stosd ; The config file is always from the server
-
- test byte [DHCPMagic], 02h
- jz .no_option
-
- ; We got a DHCP option, try it first
- mov si,trying_msg
- call writestr
- ; mov di,ConfigName ; - already the case
- mov si,di
- call writestr
- call crlf
- mov di,ConfigServer
- call open
- jnz .success
-
- .no_option:
- mov di,ConfigName
- mov si,cfgprefix
- mov cx,cfgprefix_len
- rep movsb
-
- ; Try loading by MAC address
- ; Have to guess config file name
- push di
- mov si,MACStr
- mov cx,(3*17+1)/2
- rep movsw
- mov si,trying_msg
- call writestr
- mov di,ConfigName
- mov si,di
- call writestr
- call crlf
- mov di,ConfigServer
- call open
- pop di
- jnz .success
-
- .scan_ip:
- mov cx,8
- mov eax,[MyIP]
- xchg ah,al ; Convert to host byte order
- ror eax,16
- xchg ah,al
- .hexify_loop: rol eax,4
- push eax
- and al,0Fh
- cmp al,10
- jae .high
- .low: add al,'0'
- jmp short .char
- .high: add al,'A'-10
- .char: stosb
- pop eax
- loop .hexify_loop
-
- mov cx,9 ; Up to 9 attempts
-
- .tryagain: mov byte [di],0
- cmp cx,byte 1
- jne .not_default
- pusha
- mov si,default_str
- mov cx,default_len
- rep movsb ; Copy "default" string
- popa
- .not_default: pusha
- mov si,trying_msg
- call writestr
- mov di,ConfigName
- mov si,di
- call writestr
- call crlf
- mov di,ConfigServer
- call open
- popa
- jnz .success
- dec di
- loop .tryagain
-
- jmp no_config_file
-
- .success:
-
- ;
- ; Now we have the config file open. Parse the config file and
- ; run the user interface.
- ;
- %include "ui.inc"
-
- ;
- ; Linux kernel loading code is common. However, we need to define
- ; a couple of helper macros...
- ;
-
- ; Handle "ipappend" option
- %define HAVE_SPECIAL_APPEND
- %macro SPECIAL_APPEND 0
- test byte [IPAppend],01h ; ip=
- jz .noipappend1
- mov si,IPOption
- mov cx,[IPOptionLen]
- rep movsb
- mov al,' '
- stosb
- .noipappend1:
- test byte [IPAppend],02h
- jz .noipappend2
- mov si,BOOTIFStr
- call strcpy
- mov byte [es:di-1],' ' ; Replace null with space
- .noipappend2:
- %endmacro
-
- ; Unload PXE stack
- %define HAVE_UNLOAD_PREP
- %macro UNLOAD_PREP 0
- call unload_pxe
- %endmacro
-
- %include "runkernel.inc"
-
- ;
- ; COMBOOT-loading code
- ;
- %include "comboot.inc"
- %include "com32.inc"
- %include "cmdline.inc"
-
- ;
- ; Boot sector loading code
- ;
- %include "bootsect.inc"
-
- ;
- ; Boot to the local disk by returning the appropriate PXE magic.
- ; AX contains the appropriate return code.
- ;
- local_boot:
- mov si,cs
- mov ds,si ; Restore DI
- lss esp,[BaseStack]
- mov [LocalBootType],ax
- call vgaclearmode
- mov si,localboot_msg
- call writestr
- ; Restore the environment we were called with
- lss sp,[InitStack]
- pop gs
- pop fs
- pop es
- pop ds
- popad
- mov ax,[cs:LocalBootType]
- popfd
- retf ; Return to PXE
-
- ;
- ; abort_check: let the user abort with <ESC> or <Ctrl-C>
- ;
- abort_check:
- call pollchar
- jz ac_ret1
- pusha
- call getchar
- cmp al,27 ; <ESC>
- je ac_kill
- cmp al,3 ; <Ctrl-C>
- jne ac_ret2
- ac_kill: mov si,aborted_msg
-
- ;
- ; abort_load: Called by various routines which wants to print a fatal
- ; error message and return to the command prompt. Since this
- ; may happen at just about any stage of the boot process, assume
- ; our state is messed up, and just reset the segment registers
- ; and the stack forcibly.
- ;
- ; SI = offset (in _text) of error message to print
- ;
- abort_load:
- mov ax,cs ; Restore CS = DS = ES
- mov ds,ax
- mov es,ax
- lss esp,[BaseStack]
- sti
- call cwritestr ; Expects SI -> error msg
- al_ok: jmp enter_command ; Return to command prompt
- ;
- ; End of abort_check
- ;
- ac_ret2: popa
- ac_ret1: ret
-
-
- ;
- ; kaboom: write a message and bail out. Wait for quite a while,
- ; or a user keypress, then do a hard reboot.
- ;
- kaboom:
- mov ax,cs
- mov es,ax
- mov ds,ax
- lss esp,[BaseStack]
- sti
- .patch: mov si,bailmsg
- call writestr ; Returns with AL = 0
- .drain: call pollchar
- jz .drained
- call getchar
- jmp short .drain
- .drained:
- mov edi,[RebootTime]
- mov al,[DHCPMagic]
- and al,09h ; Magic+Timeout
- cmp al,09h
- je .time_set
- mov edi,REBOOT_TIME
- .time_set:
- mov cx,18
- .wait1: push cx
- mov ecx,edi
- .wait2: mov dx,[BIOS_timer]
- .wait3: call pollchar
- jnz .keypress
- cmp dx,[BIOS_timer]
- je .wait3
- loop .wait2,ecx
- mov al,'.'
- call writechr
- pop cx
- loop .wait1
- .keypress:
- call crlf
- mov word [BIOS_magic],0 ; Cold reboot
- jmp 0F000h:0FFF0h ; Reset vector address
-
- ;
- ; memory_scan_for_pxe_struct:
- ;
- ; If none of the standard methods find the !PXE structure, look for it
- ; by scanning memory.
- ;
- ; On exit, if found:
- ; CF = 0, ES:BX -> !PXE structure
- ; Otherwise CF = 1, all registers saved
- ;
- memory_scan_for_pxe_struct:
- push ds
- pusha
- mov ax,cs
- mov ds,ax
- mov si,trymempxe_msg
- call writestr
- mov ax,[BIOS_fbm] ; Starting segment
- shl ax,(10-4) ; Kilobytes -> paragraphs
- ; mov ax,01000h ; Start to look here
- dec ax ; To skip inc ax
- .mismatch:
- inc ax
- cmp ax,0A000h ; End of memory
- jae .not_found
- call writehex4
- mov si,fourbs_msg
- call writestr
- mov es,ax
- mov edx,[es:0]
- cmp edx,'!PXE'
- jne .mismatch
- movzx cx,byte [es:4] ; Length of structure
- cmp cl,08h ; Minimum length
- jb .mismatch
- push ax
- xor ax,ax
- xor si,si
- .checksum: es lodsb
- add ah,al
- loop .checksum
- pop ax
- jnz .mismatch ; Checksum must == 0
- .found: mov bp,sp
- xor bx,bx
- mov [bp+8],bx ; Save BX into stack frame (will be == 0)
- mov ax,es
- call writehex4
- call crlf
- popa
- pop ds
- clc
- ret
- .not_found: mov si,notfound_msg
- call writestr
- popa
- pop ds
- stc
- ret
-
- ;
- ; memory_scan_for_pxenv_struct:
- ;
- ; If none of the standard methods find the PXENV+ structure, look for it
- ; by scanning memory.
- ;
- ; On exit, if found:
- ; CF = 0, ES:BX -> PXENV+ structure
- ; Otherwise CF = 1, all registers saved
- ;
- memory_scan_for_pxenv_struct:
- pusha
- mov si,trymempxenv_msg
- call writestr
- ; mov ax,[BIOS_fbm] ; Starting segment
- ; shl ax,(10-4) ; Kilobytes -> paragraphs
- mov ax,01000h ; Start to look here
- dec ax ; To skip inc ax
- .mismatch:
- inc ax
- cmp ax,0A000h ; End of memory
- jae .not_found
- mov es,ax
- mov edx,[es:0]
- cmp edx,'PXEN'
- jne .mismatch
- mov dx,[es:4]
- cmp dx,'V+'
- jne .mismatch
- movzx cx,byte [es:8] ; Length of structure
- cmp cl,26h ; Minimum length
- jb .mismatch
- xor ax,ax
- xor si,si
- .checksum: es lodsb
- add ah,al
- loop .checksum
- and ah,ah
- jnz .mismatch ; Checksum must == 0
- .found: mov bp,sp
- mov [bp+8],bx ; Save BX into stack frame
- mov ax,bx
- call writehex4
- call crlf
- clc
- ret
- .not_found: mov si,notfound_msg
- call writestr
- popad
- stc
- ret
-
- ;
- ; searchdir:
- ;
- ; Open a TFTP connection to the server
- ;
- ; On entry:
- ; DS:DI = mangled filename
- ; If successful:
- ; ZF clear
- ; SI = socket pointer
- ; DX:AX = file length in bytes
- ; If unsuccessful
- ; ZF set
- ;
-
- searchdir:
- push es
- mov ax,ds
- mov es,ax
- mov si,di
- push bp
- mov bp,sp
-
- call allocate_socket
- jz .error
-
- mov ax,PKT_RETRY ; Retry counter
- mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
-
- .sendreq: push ax ; [bp-2] - Retry counter
- push si ; [bp-4] - File name
-
- mov di,packet_buf
- mov [pxe_udp_write_pkt.buffer],di
-
- mov ax,TFTP_RRQ ; TFTP opcode
- stosw
-
- lodsd ; EAX <- server override (if any)
- and eax,eax
- jnz .noprefix ; No prefix, and we have the server
-
- push si ; Add common prefix
- mov si,PathPrefix
- call strcpy
- dec di
- pop si
-
- mov eax,[ServerIP] ; Get default server
-
- .noprefix:
- call strcpy ; Filename
-
- mov [bx+tftp_remoteip],eax
-
- push bx ; [bp-6] - TFTP block
- mov bx,[bx]
- push bx ; [bp-8] - TID (local port no)
-
- mov [pxe_udp_write_pkt.status],byte 0
- mov [pxe_udp_write_pkt.sip],eax
- ; Now figure out the gateway
- xor eax,[MyIP]
- and eax,[Netmask]
- jz .nogwneeded
- mov eax,[Gateway]
- .nogwneeded:
- mov [pxe_udp_write_pkt.gip],eax
- mov [pxe_udp_write_pkt.lport],bx
- mov ax,[ServerPort]
- mov [pxe_udp_write_pkt.rport],ax
- mov si,tftp_tail
- mov cx,tftp_tail_len
- rep movsb
- sub di,packet_buf ; Get packet size
- mov [pxe_udp_write_pkt.buffersize],di
-
- mov di,pxe_udp_write_pkt
- mov bx,PXENV_UDP_WRITE
- call pxenv
- jc .failure
- cmp word [pxe_udp_write_pkt.status],byte 0
- jne .failure
-
- ;
- ; Danger, Will Robinson! We need to support timeout
- ; and retry lest we just lost a packet...
- ;
-
- ; Packet transmitted OK, now we need to receive
- .getpacket: push word [PktTimeout] ; [bp-10]
- push word [BIOS_timer] ; [bp-12]
-
- .pkt_loop: mov bx,[bp-8] ; TID
- mov di,packet_buf
- mov word [pxe_udp_read_pkt.status],0
- mov [pxe_udp_read_pkt.buffer],di
- mov [pxe_udp_read_pkt.buffer+2],ds
- mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
- mov eax,[MyIP]
- mov [pxe_udp_read_pkt.dip],eax
- mov [pxe_udp_read_pkt.lport],bx
- mov di,pxe_udp_read_pkt
- mov bx,PXENV_UDP_READ
- call pxenv
- and ax,ax
- jz .got_packet ; Wait for packet
- .no_packet:
- mov dx,[BIOS_timer]
- cmp dx,[bp-12]
- je .pkt_loop
- mov [bp-12],dx
- dec word [bp-10] ; Timeout
- jnz .pkt_loop
- pop ax ; Adjust stack
- pop ax
- shl word [PktTimeout],1 ; Exponential backoff
- jmp .failure
-
- .got_packet:
- mov si,[bp-6] ; TFTP pointer
- mov bx,[bp-8] ; TID
-
- mov eax,[si+tftp_remoteip]
- cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec?
- jne .no_packet
-
- ; Got packet - reset timeout
- mov word [PktTimeout],PKT_TIMEOUT
-
- pop ax ; Adjust stack
- pop ax
-
- mov ax,[pxe_udp_read_pkt.rport]
- mov [si+tftp_remoteport],ax
-
- ; filesize <- -1 == unknown
- mov dword [si+tftp_filesize], -1
- ; Default blksize unless blksize option negotiated
- mov word [si+tftp_blksize], TFTP_BLOCKSIZE
-
- mov cx,[pxe_udp_read_pkt.buffersize]
- sub cx,2 ; CX <- bytes after opcode
- jb .failure ; Garbled reply
-
- mov si,packet_buf
- lodsw
-
- cmp ax, TFTP_ERROR
- je .bailnow ; ERROR reply: don't try again
-
- cmp ax, TFTP_OACK
- jne .no_tsize
-
- ; Now we need to parse the OACK packet to get the transfer
- ; size. SI -> first byte of options; CX -> byte count
- .parse_oack:
- jcxz .no_tsize ; No options acked
- .get_opt_name:
- mov di,si
- mov bx,si
- .opt_name_loop: lodsb
- and al,al
- jz .got_opt_name
- or al,20h ; Convert to lowercase
- stosb
- loop .opt_name_loop
- ; We ran out, and no final null
- jmp .err_reply
- .got_opt_name: ; si -> option value
- dec cx ; bytes left in pkt
- jz .err_reply ; Option w/o value
-
- ; Parse option pointed to by bx; guaranteed to be
- ; null-terminated.
- push cx
- push si
- mov si,bx ; -> option name
- mov bx,tftp_opt_table
- mov cx,tftp_opts
- .opt_loop:
- push cx
- push si
- mov di,[bx] ; Option pointer
- mov cx,[bx+2] ; Option len
- repe cmpsb
- pop si
- pop cx
- je .get_value ; OK, known option
- add bx,6
- loop .opt_loop
-
- pop si
- pop cx
- jmp .err_reply ; Non-negotiated option returned
-
- .get_value: pop si ; si -> option value
- pop cx ; cx -> bytes left in pkt
- mov bx,[bx+4] ; Pointer to data target
- add bx,[bp-6] ; TFTP socket pointer
- xor eax,eax
- xor edx,edx
- .value_loop: lodsb
- and al,al
- jz .got_value
- sub al,'0'
- cmp al, 9
- ja .err_reply ; Not a decimal digit
- imul edx,10
- add edx,eax
- mov [bx],edx
- loop .value_loop
- ; Ran out before final null, accept anyway
- jmp short .done_pkt
-
- .got_value:
- dec cx
- jnz .get_opt_name ; Not end of packet
-
- ; ZF == 1
-
- ; Success, done!
- .done_pkt:
- pop si ; Junk
- pop si ; We want the packet ptr in SI
-
- mov eax,[si+tftp_filesize]
- cmp eax,-1
- jz .no_tsize
- mov edx,eax
- shr edx,16 ; DX:AX == EAX
-
- and eax,eax ; Set ZF depending on file size
- pop bp ; Junk
- pop bp ; Junk (retry counter)
- jz .error_si ; ZF = 1 need to free the socket
- pop bp
- pop es
- ret
-
- .no_tsize:
- .err_reply: ; Option negotiation error. Send ERROR reply.
- ; ServerIP and gateway are already programmed in
- mov si,[bp-6]
- mov ax,[si+tftp_remoteport]
- mov word [pxe_udp_write_pkt.rport],ax
- mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
- mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
- mov di,pxe_udp_write_pkt
- mov bx,PXENV_UDP_WRITE
- call pxenv
-
- ; Write an error message and explode
- mov si,err_oldtftp
- call writestr
- jmp kaboom
-
- .bailnow: mov word [bp-2],1 ; Immediate error - no retry
-
- .failure: pop bx ; Junk
- pop bx
- pop si
- pop ax
- dec ax ; Retry counter
- jnz .sendreq ; Try again
-
- .error: mov si,bx ; Socket pointer
- .error_si: ; Socket pointer already in SI
- call free_socket ; ZF <- 1, SI <- 0
- pop bp
- pop es
- ret
-
- ;
- ; allocate_socket: Allocate a local UDP port structure
- ;
- ; If successful:
- ; ZF set
- ; BX = socket pointer
- ; If unsuccessful:
- ; ZF clear
- ;
- allocate_socket:
- push cx
- mov bx,Files
- mov cx,MAX_OPEN
- .check: cmp word [bx], byte 0
- je .found
- add bx,open_file_t_size
- loop .check
- xor cx,cx ; ZF = 1
- pop cx
- ret
- ; Allocate a socket number. Socket numbers are made
- ; guaranteed unique by including the socket slot number
- ; (inverted, because we use the loop counter cx); add a
- ; counter value to keep the numbers from being likely to
- ; get immediately reused.
- ;
- ; The NextSocket variable also contains the top two bits
- ; set. This generates a value in the range 49152 to
- ; 57343.
- .found:
- dec cx
- push ax
- mov ax,[NextSocket]
- inc ax
- and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
- mov [NextSocket],ax
- shl cx,13-MAX_OPEN_LG2
- add cx,ax ; ZF = 0
- xchg ch,cl ; Convert to network byte order
- mov [bx],cx ; Socket in use
- pop ax
- pop cx
- ret
-
- ;
- ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
- ;
- free_socket:
- push es
- pusha
- xor ax,ax
- mov es,ax
- mov di,si
- mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
- rep stosw
- popa
- pop es
- xor si,si
- ret
-
- ;
- ; parse_dotquad:
- ; Read a dot-quad pathname in DS:SI and output an IP
- ; address in EAX, with SI pointing to the first
- ; nonmatching character.
- ;
- ; Return CF=1 on error.
- ;
- parse_dotquad:
- push cx
- mov cx,4
- xor eax,eax
- .parseloop:
- mov ch,ah
- mov ah,al
- lodsb
- sub al,'0'
- jb .notnumeric
- cmp al,9
- ja .notnumeric
- aad ; AL += 10 * AH; AH = 0;
- xchg ah,ch
- jmp .parseloop
- .notnumeric:
- cmp al,'.'-'0'
- pushf
- mov al,ah
- mov ah,ch
- xor ch,ch
- ror eax,8
- popf
- jne .error
- loop .parseloop
- jmp .done
- .error:
- loop .realerror ; If CX := 1 then we're done
- clc
- jmp .done
- .realerror:
- stc
- .done:
- dec si ; CF unchanged!
- pop cx
- ret
- ;
- ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
- ; to by ES:DI; ends on encountering any whitespace.
- ;
- ; This verifies that a filename is < FILENAME_MAX characters
- ; and doesn't contain whitespace, and zero-pads the output buffer,
- ; so "repe cmpsb" can do a compare.
- ;
- ; The first four bytes of the manged name is the IP address of
- ; the download host.
- ;
- mangle_name:
- push si
- mov eax,[ServerIP]
- cmp byte [si],0
- je .noip ; Null filename?!?!
- cmp word [si],'::' ; Leading ::?
- je .gotprefix
-
- .more:
- inc si
- cmp byte [si],0
- je .noip
- cmp word [si],'::'
- jne .more
-
- ; We have a :: prefix of some sort, it could be either
- ; a DNS name or a dot-quad IP address. Try the dot-quad
- ; first...
- .here:
- pop si
- push si
- call parse_dotquad
- jc .notdq
- cmp word [si],'::'
- je .gotprefix
- .notdq:
- pop si
- push si
- call dns_resolv
- cmp word [si],'::'
- jne .noip
- and eax,eax
- jnz .gotprefix
-
- .noip:
- pop si
- xor eax,eax
- jmp .prefix_done
-
- .gotprefix:
- pop cx ; Adjust stack
- inc si ; Skip double colon
- inc si
-
- .prefix_done:
- stosd ; Save IP address prefix
- mov cx,FILENAME_MAX-5
-
- .mn_loop:
- lodsb
- cmp al,' ' ; If control or space, end
- jna .mn_end
- stosb
- loop .mn_loop
- .mn_end:
- inc cx ; At least one null byte
- xor ax,ax ; Zero-fill name
- rep stosb ; Doesn't do anything if CX=0
- ret ; Done
-
- ;
- ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
- ; filename to the conventional representation. This is needed
- ; for the BOOT_IMAGE= parameter for the kernel.
- ; NOTE: A 13-byte buffer is mandatory, even if the string is
- ; known to be shorter.
- ;
- ; DS:SI -> input mangled file name
- ; ES:DI -> output buffer
- ;
- ; On return, DI points to the first byte after the output name,
- ; which is set to a null byte.
- ;
- unmangle_name:
- push eax
- lodsd
- and eax,eax
- jz .noip
- call gendotquad
- mov ax,'::'
- stosw
- .noip:
- call strcpy
- dec di ; Point to final null byte
- pop eax
- ret
-
- ;
- ; pxenv
- ;
- ; This is the main PXENV+/!PXE entry point, using the PXENV+
- ; calling convention. This is a separate local routine so
- ; we can hook special things from it if necessary.
- ;
-
- pxenv:
- .jump: call 0:pxe_thunk ; Default to calling the thunk
- cld ; Make sure DF <- 0
- ret
-
- ; Must be after function def due to NASM bug
- PXENVEntry equ pxenv.jump+1
-
- ;
- ; pxe_thunk
- ;
- ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
- ; calling convention (using the stack.)
- ;
- ; This is called as a far routine so that we can just stick it into
- ; the PXENVEntry variable.
- ;
- pxe_thunk: push es
- push di
- push bx
- .jump: call 0:0
- add sp,byte 6
- cmp ax,byte 1
- cmc ; Set CF unless ax == 0
- retf
-
- ; Must be after function def due to NASM bug
- PXEEntry equ pxe_thunk.jump+1
-
- ;
- ; getfssec: Get multiple clusters from a file, given the starting cluster.
- ;
- ; In this case, get multiple blocks from a specific TCP connection.
- ;
- ; On entry:
- ; ES:BX -> Buffer
- ; SI -> TFTP socket pointer
- ; CX -> 512-byte block count; 0FFFFh = until end of file
- ; On exit:
- ; SI -> TFTP socket pointer (or 0 on EOF)
- ; CF = 1 -> Hit EOF
- ;
- getfssec: push si
- push fs
- mov di,bx
- mov bx,si
- mov ax,pktbuf_seg
- mov fs,ax
-
- movzx ecx,cx
- shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
- jz .hit_eof ; Nothing to do?
-
- .need_more:
- push ecx
-
- movzx eax,word [bx+tftp_bytesleft]
- cmp ecx,eax
- jna .ok_size
- mov ecx,eax
- jcxz .need_packet ; No bytes available?
- .ok_size:
-
- mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
- mov si,[bx+tftp_dataptr]
- sub [bx+tftp_bytesleft],cx
- fs rep movsb ; Copy from packet buffer
- mov [bx+tftp_dataptr],si
-
- pop ecx
- sub ecx,eax
- jnz .need_more
-
-
- .hit_eof:
- pop fs
- pop si
-
- ; Is there anything left of this?
- mov eax,[si+tftp_filesize]
- sub eax,[si+tftp_filepos]
- jnz .bytes_left ; CF <- 0
-
- cmp [si+tftp_bytesleft],ax
- jnz .bytes_left ; CF <- 0
-
- ; The socket is closed and the buffer drained
- ; Close socket structure and re-init for next user
- call free_socket
- stc
- .bytes_left:
- ret
-
- ;
- ; No data in buffer, check to see if we can get a packet...
- ;
- .need_packet:
- pop ecx
- mov eax,[bx+tftp_filesize]
- cmp eax,[bx+tftp_filepos]
- je .hit_eof ; Already EOF'd; socket already closed
-
- pushad
- push es
- mov si,bx
- call get_packet
- pop es
- popad
-
- jmp .need_more
-
- ;
- ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
- ;
- get_packet:
- mov ax,ds
- mov es,ax
-
- .packet_loop:
- ; Start by ACKing the previous packet; this should cause the
- ; next packet to be sent.
- mov cx,PKT_RETRY
- mov word [PktTimeout],PKT_TIMEOUT
-
- .send_ack: push cx ; <D> Retry count
-
- mov ax,[si+tftp_lastpkt]
- call ack_packet ; Send ACK
-
- ; We used to test the error code here, but sometimes
- ; PXE would return negative status even though we really
- ; did send the ACK. Now, just treat a failed send as
- ; a normally lost packet, and let it time out in due
- ; course of events.
-
- .send_ok: ; Now wait for packet.
- mov dx,[BIOS_timer] ; Get current time
-
- mov cx,[PktTimeout]
- .wait_data: push cx ; <E> Timeout
- push dx ; <F> Old time
-
- mov bx,[si+tftp_pktbuf]
- mov [pxe_udp_read_pkt.buffer],bx
- mov [pxe_udp_read_pkt.buffer+2],fs
- mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
- mov eax,[si+tftp_remoteip]
- mov [pxe_udp_read_pkt.sip],eax
- mov eax,[MyIP]
- mov [pxe_udp_read_pkt.dip],eax
- mov ax,[si+tftp_remoteport]
- mov [pxe_udp_read_pkt.rport],ax
- mov ax,[si+tftp_localport]
- mov [pxe_udp_read_pkt.lport],ax
- mov di,pxe_udp_read_pkt
- mov bx,PXENV_UDP_READ
- push si ; <G>
- call pxenv
- pop si ; <G>
- and ax,ax
- jz .recv_ok
-
- ; No packet, or receive failure
- mov dx,[BIOS_timer]
- pop ax ; <F> Old time
- pop cx ; <E> Timeout
- cmp ax,dx ; Same time -> don't advance timeout
- je .wait_data ; Same clock tick
- loop .wait_data ; Decrease timeout
-
- pop cx ; <D> Didn't get any, send another ACK
- shl word [PktTimeout],1 ; Exponential backoff
- loop .send_ack
- jmp kaboom ; Forget it...
-
- .recv_ok: pop dx ; <F>
- pop cx ; <E>
-
- cmp word [pxe_udp_read_pkt.buffersize],byte 4
- jb .wait_data ; Bad size for a DATA packet
-
- mov bx,[si+tftp_pktbuf]
- cmp word [fs:bx],TFTP_DATA ; Not a data packet?
- jne .wait_data ; Then wait for something else
-
- mov ax,[si+tftp_lastpkt]
- xchg ah,al ; Host byte order
- inc ax ; Which packet are we waiting for?
- xchg ah,al ; Network byte order
- cmp [fs:bx+2],ax
- je .right_packet
-
- ; Wrong packet, ACK the packet and then try again
- ; This is presumably because the ACK got lost,
- ; so the server just resent the previous packet
- mov ax,[fs:bx+2]
- call ack_packet
- jmp .send_ok ; Reset timeout
-
- .right_packet: ; It's the packet we want. We're also EOF if the size < blocksize
-
- pop cx ; <D> Don't need the retry count anymore
-
- mov [si+tftp_lastpkt],ax ; Update last packet number
-
- movzx ecx,word [pxe_udp_read_pkt.buffersize]
- sub cx,byte 4 ; Skip TFTP header
-
- ; If this is a zero-length block, don't mess with the pointers,
- ; since we may have just set up the previous block that way
- jz .last_block
-
- ; Set pointer to data block
- lea ax,[bx+4] ; Data past TFTP header
- mov [si+tftp_dataptr],ax
-
- add [si+tftp_filepos],ecx
- mov [si+tftp_bytesleft],cx
-
- cmp cx,[si+tftp_blksize] ; Is it a full block?
- jb .last_block ; If so, it's not EOF
-
- ; If we had the exact right number of bytes, always get
- ; one more packet to get the (zero-byte) EOF packet and
- ; close the socket.
- mov eax,[si+tftp_filepos]
- cmp [si+tftp_filesize],eax
- je .packet_loop
-
- ret
-
-
- .last_block: ; Last block - ACK packet immediately
- mov ax,[fs:bx+2]
- call ack_packet
-
- ; Make sure we know we are at end of file
- mov eax,[si+tftp_filepos]
- mov [si+tftp_filesize],eax
-
- ret
-
- ;
- ; ack_packet:
- ;
- ; Send ACK packet. This is a common operation and so is worth canning.
- ;
- ; Entry:
- ; SI = TFTP block
- ; AX = Packet # to ack (network byte order)
- ; Exit:
- ; ZF = 0 -> Error
- ; All registers preserved
- ;
- ; This function uses the pxe_udp_write_pkt but not the packet_buf.
- ;
- ack_packet:
- pushad
- mov [ack_packet_buf+2],ax ; Packet number to ack
- mov ax,[si]
- mov [pxe_udp_write_pkt.lport],ax
- mov ax,[si+tftp_remoteport]
- mov [pxe_udp_write_pkt.rport],ax
- mov eax,[si+tftp_remoteip]
- mov [pxe_udp_write_pkt.sip],eax
- xor eax,[MyIP]
- and eax,[Netmask]
- jz .nogw
- mov eax,[Gateway]
- .nogw:
- mov [pxe_udp_write_pkt.gip],eax
- mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
- mov [pxe_udp_write_pkt.buffersize], word 4
- mov di,pxe_udp_write_pkt
- mov bx,PXENV_UDP_WRITE
- call pxenv
- cmp ax,byte 0 ; ZF = 1 if write OK
- popad
- ret
-
- ;
- ; unload_pxe:
- ;
- ; This function unloads the PXE and UNDI stacks and unclaims
- ; the memory.
- ;
- unload_pxe:
- test byte [KeepPXE],01h ; Should we keep PXE around?
- jnz reset_pxe
-
- push ds
- push es
-
- mov ax,cs
- mov ds,ax
- mov es,ax
-
- mov si,new_api_unload
- cmp byte [APIVer+1],2 ; Major API version >= 2?
- jae .new_api
- mov si,old_api_unload
- .new_api:
-
- .call_loop: xor ax,ax
- lodsb
- and ax,ax
- jz .call_done
- xchg bx,ax
- mov di,pxe_unload_stack_pkt
- push di
- xor ax,ax
- mov cx,pxe_unload_stack_pkt_len >> 1
- rep stosw
- pop di
- call pxenv
- jc .cant_free
- mov ax,word [pxe_unload_stack_pkt.status]
- cmp ax,PXENV_STATUS_SUCCESS
- jne .cant_free
- jmp .call_loop
-
- .call_done:
- ;
- ; This isn't necessary anymore; we can use the memory area previously
- ; used by the PXE stack indefinitely, and the chainload code sets up
- ; a new stack independently. Leave the source code in here for now,
- ; but expect to rip it out soonish.
- ;
- %if 0 ; USE_PXE_PROVIDED_STACK
- ; We need to switch to our local stack here...
- pusha
- pushf
- push gs
-
- mov si,sp
- mov ax,ss
- mov gs,ax
- sub ax,[BaseStack+4] ; Are we using the base stack
- je .is_base_stack ; (as opposed to the COMBOOT stack)?
-
- lgs si,[SavedSSSP] ; COMBOOT stack
- .is_base_stack:
-
- mov cx,[InitStack]
- mov di,StackBuf
- mov [BaseStack],di
- mov [BaseStack+4],es
- sub cx,si
- sub di,cx
- mov [SavedSSSP],di ; New SP
- mov [SavedSSSP+2],es
- gs rep movsb
-
- and ax,ax ; Remember which stack
- jne .combootstack
-
- ; Update the base stack pointer since it's in use
- lss sp,[SavedSSSP]
-
- .combootstack:
- pop gs
- popf
- popa
- %endif
- mov bx,0FF00h
-
- mov dx,[RealBaseMem]
- cmp dx,[BIOS_fbm] ; Sanity check
- jna .cant_free
- inc bx
-
- ; Check that PXE actually unhooked the INT 1Ah chain
- movzx eax,word [4*0x1a]
- movzx ecx,word [4*0x1a+2]
- shl ecx,4
- add eax,ecx
- shr eax,10
- cmp ax,dx ; Not in range
- jae .ok
- cmp ax,[BIOS_fbm]
- jae .cant_free
- ; inc bx
-
- .ok:
- mov [BIOS_fbm],dx
- .pop_ret:
- pop es
- pop ds
- ret
-
- .cant_free:
- mov si,cant_free_msg
- call writestr
- push ax
- xchg bx,ax
- call writehex4
- mov al,'-'
- call writechr
- pop ax
- call writehex4
- mov al,'-'
- call writechr
- mov eax,[4*0x1a]
- call writehex8
- call crlf
- jmp .pop_ret
-
- ; We want to keep PXE around, but still we should reset
- ; it to the standard bootup configuration
- reset_pxe:
- push es
- push cs
- pop es
- mov bx,PXENV_UDP_CLOSE
- mov di,pxe_udp_close_pkt
- call pxenv
- pop es
- ret
-
- ;
- ; gendotquad
- ;
- ; Take an IP address (in network byte order) in EAX and
- ; output a dotted quad string to ES:DI.
- ; DI points to terminal null at end of string on exit.
- ;
- gendotquad:
- push eax
- push cx
- mov cx,4
- .genchar:
- push eax
- cmp al,10 ; < 10?
- jb .lt10 ; If so, skip first 2 digits
-
- cmp al,100 ; < 100
- jb .lt100 ; If so, skip first digit
-
- aam 100
- ; Now AH = 100-digit; AL = remainder
- add ah,'0'
- mov [es:di],ah
- inc di
-
- .lt100:
- aam 10
- ; Now AH = 10-digit; AL = remainder
- add ah,'0'
- mov [es:di],ah
- inc di
-
- .lt10:
- add al,'0'
- stosb
- mov al,'.'
- stosb
- pop eax
- ror eax,8 ; Move next char into LSB
- loop .genchar
- dec di
- mov [es:di], byte 0
- pop cx
- pop eax
- ret
-
- ;
- ; parse_dhcp
- ;
- ; Parse a DHCP packet. This includes dealing with "overloaded"
- ; option fields (see RFC 2132, section 9.3)
- ;
- ; This should fill in the following global variables, if the
- ; information is present:
- ;
- ; MyIP - client IP address
- ; ServerIP - boot server IP address
- ; Netmask - network mask
- ; Gateway - default gateway router IP
- ; BootFile - boot file name
- ; DNSServers - DNS server IPs
- ; LocalDomain - Local domain name
- ;
- ; This assumes the DHCP packet is in "trackbuf" and the length
- ; of the packet in in CX on entry.
- ;
-
- parse_dhcp:
- mov byte [OverLoad],0 ; Assume no overload
- mov eax, [trackbuf+bootp.yip]
- and eax, eax
- jz .noyip
- cmp al,224 ; Class D or higher -> bad
- jae .noyip
- mov [MyIP], eax
- .noyip:
- mov eax, [trackbuf+bootp.sip]
- and eax, eax
- jz .nosip
- cmp al,224 ; Class D or higher -> bad
- jae .nosip
- mov [ServerIP], eax
- .nosip:
- sub cx, bootp.options
- jbe .nooptions
- mov si, trackbuf+bootp.option_magic
- lodsd
- cmp eax, BOOTP_OPTION_MAGIC
- jne .nooptions
- call parse_dhcp_options
- .nooptions:
- mov si, trackbuf+bootp.bootfile
- test byte [OverLoad],1
- jz .nofileoverload
- mov cx,128
- call parse_dhcp_options
- jmp short .parsed_file
- .nofileoverload:
- cmp byte [si], 0
- jz .parsed_file ; No bootfile name
- mov di,BootFile
- mov cx,32
- rep movsd
- xor al,al
- stosb ; Null-terminate
- .parsed_file:
- mov si, trackbuf+bootp.sname
- test byte [OverLoad],2
- jz .nosnameoverload
- mov cx,64
- call parse_dhcp_options
- .nosnameoverload:
- ret
-
- ;
- ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
- ; size is CX -- some DHCP servers leave option fields unterminated
- ; in violation of the spec.
- ;
- ; For parse_some_dhcp_options, DH contains the minimum value for
- ; the option to recognize -- this is used to restrict parsing to
- ; PXELINUX-specific options only.
- ;
- parse_dhcp_options:
- xor dx,dx
-
- parse_some_dhcp_options:
- .loop:
- and cx,cx
- jz .done
-
- lodsb
- dec cx
- jz .done ; Last byte; must be PAD, END or malformed
- cmp al, 0 ; PAD option
- je .loop
- cmp al,255 ; END option
- je .done
-
- ; Anything else will have a length field
- mov dl,al ; DL <- option number
- xor ax,ax
- lodsb ; AX <- option length
- dec cx
- sub cx,ax ; Decrement bytes left counter
- jb .done ; Malformed option: length > field size
-
- cmp dl,dh ; Is the option value valid?
- jb .opt_done
-
- cmp dl,1 ; SUBNET MASK option
- jne .not_subnet
- mov ebx,[si]
- mov [Netmask],ebx
- jmp .opt_done
- .not_subnet:
-
- cmp dl,3 ; ROUTER option
- jne .not_router
- mov ebx,[si]
- mov [Gateway],ebx
- jmp .opt_done
- .not_router:
-
- cmp dl,6 ; DNS SERVERS option
- jne .not_dns
- pusha
- mov cx,ax
- shr cx,2
- cmp cl,DNS_MAX_SERVERS
- jna .oklen
- mov cl,DNS_MAX_SERVERS
- .oklen:
- mov di,DNSServers
- rep movsd
- mov [LastDNSServer],di
- popa
- jmp .opt_done
- .not_dns:
-
- cmp dl,15 ; DNS LOCAL DOMAIN option
- jne .not_localdomain
- pusha
- mov bx,si
- add bx,ax
- xor ax,ax
- xchg [bx],al ; Zero-terminate option
- mov di,LocalDomain
- call dns_mangle ; Convert to DNS label set
- mov [bx],al ; Restore ending byte
- popa
- jmp .opt_done
- .not_localdomain:
-
- cmp dl,43 ; VENDOR ENCAPSULATED option
- jne .not_vendor
- pusha
- mov dh,208 ; Only recognize PXELINUX options
- mov cx,ax ; Length of option = max bytes to parse
- call parse_some_dhcp_options ; Parse recursive structure
- popa
- jmp .opt_done
- .not_vendor:
-
- cmp dl,52 ; OPTION OVERLOAD option
- jne .not_overload
- mov bl,[si]
- mov [OverLoad],bl
- jmp .opt_done
- .not_overload:
-
- cmp dl,67 ; BOOTFILE NAME option
- jne .not_bootfile
- mov di,BootFile
- jmp short .copyoption
- .done:
- ret ; This is here to make short jumps easier
- .not_bootfile:
-
- cmp dl,208 ; PXELINUX MAGIC option
- jne .not_pl_magic
- cmp al,4 ; Must have length == 4
- jne .opt_done
- cmp dword [si], htonl(0xF100747E) ; Magic number
- jne .opt_done
- or byte [DHCPMagic], byte 1 ; Found magic #
- jmp short .opt_done
- .not_pl_magic:
-
- cmp dl,209 ; PXELINUX CONFIGFILE option
- jne .not_pl_config
- mov di,ConfigName
- or byte [DHCPMagic], byte 2 ; Got config file
- jmp short .copyoption
- .not_pl_config:
-
- cmp dl,210 ; PXELINUX PATHPREFIX option
- jne .not_pl_prefix
- mov di,PathPrefix
- or byte [DHCPMagic], byte 4 ; Got path prefix
- jmp short .copyoption
- .not_pl_prefix:
-
- cmp dl,211 ; PXELINUX REBOOTTIME option
- jne .not_pl_timeout
- cmp al,4
- jne .opt_done
- mov ebx,[si]
- xchg bl,bh ; Convert to host byte order
- rol ebx,16
- xchg bl,bh
- mov [RebootTime],ebx
- or byte [DHCPMagic], byte 8 ; Got RebootTime
- ; jmp short .opt_done
- .not_pl_timeout:
-
- ; Unknown option. Skip to the next one.
- .opt_done:
- add si,ax
- .opt_done_noskip:
- jmp .loop
-
- ; Common code for copying an option verbatim
- .copyoption:
- xchg cx,ax
- rep movsb
- xchg cx,ax ; Now ax == 0
- stosb ; Null-terminate
- jmp short .opt_done_noskip
-
- ;
- ; genipopt
- ;
- ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
- ; option into IPOption based on a DHCP packet in trackbuf.
- ; Assumes CS == DS == ES.
- ;
- genipopt:
- pushad
- mov di,IPOption
- mov eax,'ip='
- stosd
- dec di
- mov eax,[MyIP]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[ServerIP]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[Gateway]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[Netmask]
- call gendotquad ; Zero-terminates its output
- sub di,IPOption
- mov [IPOptionLen],di
- popad
- ret
-
- ;
- ; Call the receive loop while idle. This is done mostly so we can respond to
- ; ARP messages, but perhaps in the future this can be used to do network
- ; console.
- ;
- ; hpa sez: people using automatic control on the serial port get very
- ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
- ; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
- ; passed since the last poll, and reset this when a character is
- ; received (RESET_IDLE).
- ;
- reset_idle:
- push ax
- mov ax,[cs:BIOS_timer]
- mov [cs:IdleTimer],ax
- pop ax
- ret
-
- check_for_arp:
- push ax
- mov ax,[cs:BIOS_timer]
- sub ax,[cs:IdleTimer]
- cmp ax,4
- pop ax
- jae .need_poll
- ret
- .need_poll: pushad
- push ds
- push es
- mov ax,cs
- mov ds,ax
- mov es,ax
- mov di,packet_buf
- mov [pxe_udp_read_pkt.status],al ; 0
- mov [pxe_udp_read_pkt.buffer],di
- mov [pxe_udp_read_pkt.buffer+2],ds
- mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
- mov eax,[MyIP]
- mov [pxe_udp_read_pkt.dip],eax
- mov [pxe_udp_read_pkt.lport],ax ; 0
- mov di,pxe_udp_read_pkt
- mov bx,PXENV_UDP_READ
- call pxenv
- ; Ignore result...
- pop es
- pop ds
- popad
- RESET_IDLE
- ret
-
- ; -----------------------------------------------------------------------------
- ; Common modules
- ; -----------------------------------------------------------------------------
-
- %include "getc.inc" ; getc et al
- %include "conio.inc" ; Console I/O
- %include "writestr.inc" ; String output
- writestr equ cwritestr
- %include "writehex.inc" ; Hexadecimal output
- %include "parseconfig.inc" ; High-level config file handling
- %include "parsecmd.inc" ; Low-level config file handling
- %include "bcopy32.inc" ; 32-bit bcopy
- %include "loadhigh.inc" ; Load a file into high memory
- %include "font.inc" ; VGA font stuff
- %include "graphics.inc" ; VGA graphics
- %include "highmem.inc" ; High memory sizing
- %include "strcpy.inc" ; strcpy()
- %include "rawcon.inc" ; Console I/O w/o using the console functions
- %include "dnsresolv.inc" ; DNS resolver
-
- ; -----------------------------------------------------------------------------
- ; Begin data section
- ; -----------------------------------------------------------------------------
-
- section .data
-
- hextbl_lower db '0123456789abcdef'
- copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
- db CR, LF, 0
- boot_prompt db 'boot: ', 0
- wipe_char db BS, ' ', BS, 0
- err_notfound db 'Could not find kernel image: ',0
- err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
- err_noram db 'It appears your computer has less than '
- asciidec dosram_k
- db 'K of low ("DOS")'
- db CR, LF
- db 'RAM. Linux needs at least this amount to boot. If you get'
- db CR, LF
- db 'this message in error, hold down the Ctrl key while'
- db CR, LF
- db 'booting, and I will take your word for it.', CR, LF, 0
- err_badcfg db 'Unknown keyword in config file.', CR, LF, 0
- err_noparm db 'Missing parameter in config file.', CR, LF, 0
- err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
- err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
- err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
- err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
- db CR, LF, 0
- err_notdos db ': attempted DOS system call', CR, LF, 0
- err_comlarge db 'COMBOOT image too large.', CR, LF, 0
- err_bssimage db 'BSS images not supported.', CR, LF, 0
- err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
- err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
- bailmsg equ err_bootfailed
- err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
- err_pxefailed db 'PXE API call failed, error ', 0
- err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
- err_oldtftp db 'TFTP server does not support the tsize option', CR, LF, 0
- found_pxenv db 'Found PXENV+ structure', CR, LF, 0
- using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
- apiver_str db 'PXE API version is ',0
- pxeentry_msg db 'PXE entry point found (we hope) at ', 0
- pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
- trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
- trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
- undi_data_msg db 'UNDI data segment at: ',0
- undi_data_len_msg db 'UNDI data segment size: ',0
- undi_code_msg db 'UNDI code segment at: ',0
- undi_code_len_msg db 'UNDI code segment size: ',0
- cant_free_msg db 'Failed to free base memory, error ', 0
- notfound_msg db 'not found', CR, LF, 0
- myipaddr_msg db 'My IP address seems to be ',0
- tftpprefix_msg db 'TFTP prefix: ', 0
- localboot_msg db 'Booting from local disk...', CR, LF, 0
- cmdline_msg db 'Command line: ', CR, LF, 0
- ready_msg db 'Ready.', CR, LF, 0
- trying_msg db 'Trying to load: ', 0
- crlfloading_msg db CR, LF ; Fall through
- loading_msg db 'Loading ', 0
- dotdot_msg db '.'
- dot_msg db '.', 0
- fourbs_msg db BS, BS, BS, BS, 0
- aborted_msg db ' aborted.' ; Fall through to crlf_msg!
- crlf_msg db CR, LF
- null_msg db 0
- crff_msg db CR, FF, 0
- default_str db 'default', 0
- default_len equ ($-default_str)
- syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
- cfgprefix db 'pxelinux.cfg/' ; No final null!
- cfgprefix_len equ ($-cfgprefix)
-
- ;
- ; Command line options we'd like to take a look at
- ;
- ; mem= and vga= are handled as normal 32-bit integer values
- initrd_cmd db 'initrd='
- initrd_cmd_len equ $-initrd_cmd
-
- ; This one we make ourselves
- bootif_str db 'BOOTIF='
- bootif_str_len equ $-bootif_str
- ;
- ; Config file keyword table
- ;
- %include "keywords.inc"
-
- ;
- ; Extensions to search for (in *forward* order).
- ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
- ;
- align 4, db 0
- exten_table: db '.cbt' ; COMBOOT (specific)
- db '.0', 0, 0 ; PXE bootstrap program
- db '.com' ; COMBOOT (same as DOS)
- db '.c32' ; COM32
- exten_table_end:
- dd 0, 0 ; Need 8 null bytes here
-
- ;
- ; PXE unload sequences
- ;
- new_api_unload:
- db PXENV_UDP_CLOSE
- db PXENV_UNDI_SHUTDOWN
- db PXENV_UNLOAD_STACK
- db PXENV_STOP_UNDI
- db 0
- old_api_unload:
- db PXENV_UDP_CLOSE
- db PXENV_UNDI_SHUTDOWN
- db PXENV_UNLOAD_STACK
- db PXENV_UNDI_CLEANUP
- db 0
-
- ;
- ; PXE query packets partially filled in
- ;
- pxe_bootp_query_pkt_2:
- .status: dw 0 ; Status
- .packettype: dw 2 ; DHCPACK packet
- .buffersize: dw trackbufsize ; Packet size
- .buffer: dw trackbuf, 0 ; seg:off of buffer
- .bufferlimit: dw trackbufsize ; Unused
-
- pxe_bootp_query_pkt_3:
- .status: dw 0 ; Status
- .packettype: dw 3 ; Boot server packet
- .buffersize: dw trackbufsize ; Packet size
- .buffer: dw trackbuf, 0 ; seg:off of buffer
- .bufferlimit: dw trackbufsize ; Unused
-
- pxe_bootp_size_query_pkt:
- .status: dw 0 ; Status
- .packettype: dw 2 ; DHCPACK packet
- .buffersize: dw 0 ; Packet size
- .buffer: dw 0, 0 ; seg:off of buffer
- .bufferlimit: dw 0 ; Unused
-
- pxe_udp_open_pkt:
- .status: dw 0 ; Status
- .sip: dd 0 ; Source (our) IP
-
- pxe_udp_close_pkt:
- .status: dw 0 ; Status
-
- pxe_udp_write_pkt:
- .status: dw 0 ; Status
- .sip: dd 0 ; Server IP
- .gip: dd 0 ; Gateway IP
- .lport: dw 0 ; Local port
- .rport: dw 0 ; Remote port
- .buffersize: dw 0 ; Size of packet
- .buffer: dw 0, 0 ; seg:off of buffer
-
- pxe_udp_read_pkt:
- .status: dw 0 ; Status
- .sip: dd 0 ; Source IP
- .dip: dd 0 ; Destination (our) IP
- .rport: dw 0 ; Remote port
- .lport: dw 0 ; Local port
- .buffersize: dw 0 ; Max packet size
- .buffer: dw 0, 0 ; seg:off of buffer
-
- ;
- ; Misc initialized (data) variables
- ;
- alignb 4, db 0
- BaseStack dd StackBuf ; SS:ESP of base stack
- NextSocket dw 49152 ; Counter for allocating socket numbers
- KeepPXE db 0 ; Should PXE be kept around?
-
- ;
- ; TFTP commands
- ;
- tftp_tail db 'octet', 0 ; Octet mode
- tsize_str db 'tsize' ,0 ; Request size
- tsize_len equ ($-tsize_str)
- db '0', 0
- blksize_str db 'blksize', 0 ; Request large blocks
- blksize_len equ ($-blksize_str)
- asciidec TFTP_LARGEBLK
- db 0
- tftp_tail_len equ ($-tftp_tail)
-
- alignb 2, db 0
- ;
- ; Options negotiation parsing table (string pointer, string len, offset
- ; into socket structure)
- ;
- tftp_opt_table:
- dw tsize_str, tsize_len, tftp_filesize
- dw blksize_str, blksize_len, tftp_blksize
- tftp_opts equ ($-tftp_opt_table)/6
-
- ;
- ; Error packet to return on options negotiation error
- ;
- tftp_opt_err dw TFTP_ERROR ; ERROR packet
- dw TFTP_EOPTNEG ; ERROR 8: bad options
- db 'tsize option required', 0 ; Error message
- tftp_opt_err_len equ ($-tftp_opt_err)
-
- alignb 4, db 0
- ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
-
- ;
- ; IP information (initialized to "unknown" values)
- MyIP dd 0 ; My IP address
- ServerIP dd 0 ; IP address of boot server
- Netmask dd 0 ; Netmask of this subnet
- Gateway dd 0 ; Default router
- ServerPort dw TFTP_PORT ; TFTP server port
-
- ;
- ; Variables that are uninitialized in SYSLINUX but initialized here
- ;
- alignb 4, db 0
- BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
- BufSafeSec dw trackbufsize/512 ; = how many sectors?
- BufSafeBytes dw trackbufsize ; = how many bytes?
- EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
- %ifndef DEPEND
- %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
- %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
- %endif
- %endif
- IPAppend db 0 ; Default IPAPPEND option
- DHCPMagic db 0 ; DHCP site-specific option info
-